package com.hitqz.robot.driver.listener;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.hitqz.robot.api.business.enums.FormConfigEnum;
import com.hitqz.robot.api.business.enums.RobotParamsFormFieldEnum;
import com.hitqz.robot.api.business.event.FormUpdateEvent;
import com.hitqz.robot.api.common.dto.form.FormFieldConfigUpdateDto;
import com.hitqz.robot.driver.communication.NetworkServiceThreadPool;
import com.hitqz.robot.driver.communication.TcpClient;
import com.hitqz.robot.driver.dto.ObstacleAvoidanceEnableDto;
import com.hitqz.robot.driver.dto.UltrasonicDistanceConfigDto;
import com.hitqz.robot.driver.netty.handler.IpBlacklistHandler;
import com.hitqz.robot.driver.netty.handler.TrackMessageHandler;
import com.hitqz.robot.driver.netty.protocol.decoder.TrackDecoder;
import com.hitqz.robot.driver.netty.protocol.encoder.TrackEncoder;
import com.hitqz.robot.driver.util.TrackToolkit;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.logging.LoggingHandler;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import javax.sound.midi.Track;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
@AllArgsConstructor
@Slf4j
public class TrackParamsListener {

    @EventListener
    public void onParamsUpdate(FormUpdateEvent event) {
        NetworkServiceThreadPool.dealEvent(() -> {
            String formName = event.getFormName();
            if (!Objects.equals(formName, FormConfigEnum.ROBOT_PARAMS_CONFIG.getFormName())) {
                return;
            }
            List<FormFieldConfigUpdateDto> fields = event.getUpdateFields();
            try {
                this.updateRobotParams(fields);
            } catch (Exception e) {
                log.error("[track]机器人参数配置更新异常", e);
            }
            log.info("[track]机器人参数配置更新");
        });
    }


    private void updateRobotParams(List<FormFieldConfigUpdateDto> fields) throws InterruptedException {
        if (fields.isEmpty()) {
            return;
        }
        // 不需要下发到嵌入式板的,直接设置到本地缓存
        for (FormFieldConfigUpdateDto field : CollUtil.emptyIfNull(fields)) {
            try {
                String name = field.getFieldName();
                RobotParamsFormFieldEnum fieldEnum = RobotParamsFormFieldEnum.fromFieldName(name);
                if (fieldEnum == null) {
                    continue;
                }
                switch (fieldEnum) {
                    case CHARGER_IP:
                        TrackToolkit.chargerIp = field.getCurrentValue();
                        break;
                    case CHARGER_PORT:
                        TrackToolkit.chargerPort = Integer.parseInt(field.getCurrentValue());
                        break;
                    case RELAY_IP:
                        TrackToolkit.relayIp = field.getCurrentValue();
                        break;
                    case RELAY_PORT:
                        TrackToolkit.relayPort = Integer.parseInt(field.getCurrentValue());
                        break;
                    case TRACK_LOCATE_METHOD:
                        if (Objects.equals(field.getCurrentValue(), "rfid") || Objects.equals(field.getCurrentValue(), "mileage")) {
                            TrackToolkit.locationMethod = field.getCurrentValue();
                        }
                        break;
                    case ROBOT_SPEED:
                        TrackToolkit.runningSpeed = Math.min(Math.max(10, Integer.parseInt(field.getCurrentValue())), 50);
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                log.error("[track]机器人参数更新异常", e);
            }
        }
        // 创建坞站监听客户端
        if (TrackToolkit.chargerClient == null
                || !Objects.equals(TrackToolkit.chargerClient.getPort(), TrackToolkit.chargerPort)
                || !Objects.equals(TrackToolkit.chargerClient.getHost(), TrackToolkit.chargerIp)) {
            if (TrackToolkit.chargerClient != null) {
                TrackToolkit.chargerClient.shutdown();
                TrackToolkit.chargerClient = null;
            }
            NetworkServiceThreadPool.creatNetworkServerOrClient(() -> {
                try {
                    if (StrUtil.isBlank(TrackToolkit.chargerIp) || TrackToolkit.chargerPort==null){
                        return;
                    }
                    TcpClient chargeClient = new TcpClient("charger-client", TrackToolkit.chargerIp, TrackToolkit.chargerPort, new ChannelInitializer<Channel>() {
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
                            channel.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, 1, 0));
                            channel.pipeline().addLast("decoder", new TrackDecoder());
                            channel.pipeline().addLast("encoder", new TrackEncoder());
                            channel.pipeline().addLast("handler", new TrackMessageHandler());
                        }
                    });
                    TrackToolkit.chargerClient = chargeClient;
                    chargeClient.connectAsync();
                } catch (Exception e) {
                    log.info("[track]charge client error", e);
                }
            });
        }
        // 创建远程继电器客户端
        if (TrackToolkit.relayClient == null
                ||!Objects.equals(TrackToolkit.relayClient.getPort(), TrackToolkit.relayPort)
                || !Objects.equals(TrackToolkit.relayClient.getHost(), TrackToolkit.relayIp)) {
            if (TrackToolkit.relayClient != null) {
                TrackToolkit.relayClient.shutdown();
                TrackToolkit.relayClient = null;
            }
            NetworkServiceThreadPool.creatNetworkServerOrClient(() -> {
                //todo handler
                if (StrUtil.isBlank(TrackToolkit.relayIp) || TrackToolkit.relayPort==null){
                    return;
                }
                TcpClient relayClient = new TcpClient("relay-client", TrackToolkit.relayIp, TrackToolkit.relayPort, new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        channel.pipeline().addLast("logHandler", new LoggingHandler());
                        channel.pipeline().addLast("encoder",new ByteArrayEncoder());
                        channel.pipeline().addLast("decoder",new ByteArrayDecoder());
                    }
                });
                try {
                    TrackToolkit.relayClient = relayClient;
                    relayClient.connectAsync();
                } catch (Exception e) {
                    log.info("[track]relayClient client error", e);
                }
            });
        }
        // 需要设置到嵌入式板的,先验证通讯
        int waitTimes = 60;
        for (int i = 0; i < waitTimes; i++) {
            if (TrackToolkit.ctx != null) {
                break;
            } else if (i == waitTimes-1) {
                log.warn("[track] update embedded params failed, ctx is null");
                return;
            }
            TimeUnit.SECONDS.sleep(1);
        }
        boolean sensorEnableChanged = false;
        boolean ultrasonicDistanceChanged = false;
        UltrasonicDistanceConfigDto ultrasonicDistance = TrackToolkit.getUltrasonicDistance();
        ObstacleAvoidanceEnableDto obstacleAvoidanceEnable = TrackToolkit.getObstacleAvoidanceEnable();
        for (FormFieldConfigUpdateDto field : fields) {
            try {
                String name = field.getFieldName();
                RobotParamsFormFieldEnum fieldEnum = RobotParamsFormFieldEnum.fromFieldName(name);
                if (fieldEnum == null) {
                    continue;
                }
                switch (fieldEnum) {
                    case ULTRASONIC_ENABLE:
                        sensorEnableChanged = sensorEnableChanged || obstacleAvoidanceEnable.getUltrasonicEnable() != Byte.parseByte(field.getCurrentValue());
                        obstacleAvoidanceEnable.setUltrasonicEnable(Byte.parseByte(field.getCurrentValue()));
                        break;
                    case COLLISION_AVOIDANCE_ENABLED:
                        sensorEnableChanged = sensorEnableChanged || obstacleAvoidanceEnable.getCollisionAvoidanceEnabled() != Byte.parseByte(field.getCurrentValue());
                        obstacleAvoidanceEnable.setCollisionAvoidanceEnabled(Byte.parseByte(field.getCurrentValue()));
                        break;
                    case DROP_AVOIDANCE_ENABLED:
                        sensorEnableChanged = sensorEnableChanged || obstacleAvoidanceEnable.getDropAvoidanceEnabled() != Byte.parseByte(field.getCurrentValue());
                        obstacleAvoidanceEnable.setDropAvoidanceEnabled(Byte.parseByte(field.getCurrentValue()));
                        break;
                    case INFRARED_AVOIDANCE_ENABLED:
                        sensorEnableChanged = sensorEnableChanged || obstacleAvoidanceEnable.getInfraredObstacleEnabled() != Byte.parseByte(field.getCurrentValue());
                        obstacleAvoidanceEnable.setInfraredObstacleEnabled(Byte.parseByte(field.getCurrentValue()));
                        break;
                    case SAFETY_AVOIDANCE_ENABLED:
                        sensorEnableChanged = sensorEnableChanged || obstacleAvoidanceEnable.getSafetyRadarEnabled() != Byte.parseByte(field.getCurrentValue());
                        obstacleAvoidanceEnable.setSafetyRadarEnabled(Byte.parseByte(field.getCurrentValue()));
                        break;
                    case FRONT_ULTRASONIC_TRIGGER_DISTANCE:
                        ultrasonicDistanceChanged = ultrasonicDistanceChanged || ultrasonicDistance.getFrontUltrasonicTriggerDistance() != Short.parseShort(field.getCurrentValue());
                        ultrasonicDistance.setFrontUltrasonicTriggerDistance(Short.parseShort(field.getCurrentValue()));
                        break;
                    case FRONT_ULTRASONIC_RELEASE_DISTANCE:
                        ultrasonicDistanceChanged = ultrasonicDistanceChanged || ultrasonicDistance.getFrontUltrasonicReleaseDistance() != Short.parseShort(field.getCurrentValue());
                        ultrasonicDistance.setFrontUltrasonicReleaseDistance(Short.parseShort(field.getCurrentValue()));
                        break;
                    case BACK_ULTRASONIC_TRIGGER_DISTANCE:
                        ultrasonicDistanceChanged = ultrasonicDistanceChanged || ultrasonicDistance.getBackUltrasonicTriggerDistance() != Short.parseShort(field.getCurrentValue());
                        ultrasonicDistance.setBackUltrasonicTriggerDistance(Short.parseShort(field.getCurrentValue()));
                        break;
                    case BACK_ULTRASONIC_RELEASE_DISTANCE:
                        ultrasonicDistanceChanged = ultrasonicDistanceChanged || ultrasonicDistance.getBackUltrasonicReleaseDistance() != Short.parseShort(field.getCurrentValue());
                        ultrasonicDistance.setBackUltrasonicReleaseDistance(Short.parseShort(field.getCurrentValue()));
                        break;
                    case ROBOT_MILEAGE_CALIBRATION_FACTOR:
                        int mileageCalibrationFactor = TrackToolkit.getMileageCalibrationFactor();
                        if (mileageCalibrationFactor != Integer.parseInt(field.getCurrentValue())) {
                            TrackToolkit.setMileageCalibrationFactor((byte) 0x02, Integer.parseInt(field.getCurrentValue()));
                        }
                        break;
                    case RFID_READER_POWER:
                        byte rfidReaderPower = TrackToolkit.getRfidReaderPower();
                        if (rfidReaderPower != Byte.parseByte(field.getCurrentValue())) {
                            TrackToolkit.setRfidReaderPower(Byte.parseByte(field.getCurrentValue()));
                            Thread.sleep(500);
                            TrackToolkit.setControlMode((byte) 0);
                            TrackToolkit.setRunningMode((byte) 0);
                        }
                    default:
                        break;
                }
            } catch (Exception e) {
                log.error("[track]机器人参数更新异常", e);
            }
        }
        if (ultrasonicDistanceChanged) {
            short[] distances = new short[]{
                    ultrasonicDistance.getFrontUltrasonicTriggerDistance(), ultrasonicDistance.getFrontUltrasonicReleaseDistance(),
                    ultrasonicDistance.getBackUltrasonicTriggerDistance(), ultrasonicDistance.getBackUltrasonicReleaseDistance()
            };
            TrackToolkit.setUltrasonicDistance(distances);
        }
        if (sensorEnableChanged) {
            TrackToolkit.setObstacleAvoidanceEnable(obstacleAvoidanceEnable.getUltrasonicEnable(),
                    obstacleAvoidanceEnable.getCollisionAvoidanceEnabled(),
                    obstacleAvoidanceEnable.getDropAvoidanceEnabled(),
                    obstacleAvoidanceEnable.getInfraredObstacleEnabled(),
                    obstacleAvoidanceEnable.getSafetyRadarEnabled());
        }

    }

}
    
